#pharmacies-widget-fr *{
box-sizing:border-box;
margin:0;
padding:0;
}
#pharmacies-widget-fr{
font-family:Arial,Helvetica,sans-serif;
background:#f4f6fb;
padding:20px 14px 35px;
direction:ltr;
text-align:left;
}
#pharmacies-widget-fr .pw-wrap{
max-width:1100px;
margin:0 auto;
}
#pharmacies-widget-fr .pw-header{
text-align:center;
margin-bottom:20px;
}
#pharmacies-widget-fr .pw-header h2{
font-size:26px;
font-weight:800;
color:#1a1a2e;
display:block;
text-align:center;
}
#pharmacies-widget-fr .pw-emoji{
font-style:normal;
font-size:26px;
}
#pharmacies-widget-fr .pw-date{
margin-top:8px;
color:#666;
font-size:14px;
}
#pharmacies-widget-fr .pw-notice{
background:#fff8e1;
border-left:4px solid #f5a623;
border-radius:8px;
padding:12px 16px;
margin-bottom:22px;
color:#7a5c00;
font-size:14px;
line-height:1.7;
}
#pharmacies-widget-fr .pw-status{
background:#fff;
border-radius:10px;
padding:14px 16px;
margin-bottom:18px;
box-shadow:0 2px 8px rgba(0,0,0,0.06);
color:#444;
font-size:14px;
}
#pharmacies-widget-fr .pw-grid{
display:block;
overflow:hidden;
}
#pharmacies-widget-fr .pw-col{
width:33.3333%;
padding:9px;
float:left;
box-sizing:border-box;
}
#pharmacies-widget-fr .pw-grid:after{
content:"";
display:table;
clear:both;
}
@media screen and (max-width:900px){
#pharmacies-widget-fr .pw-col{width:50%;}
}
@media screen and (max-width:560px){
#pharmacies-widget-fr .pw-col{width:100%;}
#pharmacies-widget-fr .pw-header h2{font-size:20px;}
}
#pharmacies-widget-fr .pw-card{
background:#fff;
border-radius:16px;
padding:18px 16px 16px;
box-shadow:0 4px 14px rgba(0,0,0,0.07);
position:relative;
overflow:hidden;
min-height:260px;
}
#pharmacies-widget-fr .pw-card:hover{
box-shadow:0 8px 22px rgba(0,0,0,0.11);
}
#pharmacies-widget-fr .pw-card-before{
position:absolute;
top:-12px;
left:-12px;
width:60px;
height:60px;
border-radius:50%;
opacity:0.08;
}
#pharmacies-widget-fr .c1 .pw-card-before{background:#e53935;}
#pharmacies-widget-fr .c2 .pw-card-before{background:#1e88e5;}
#pharmacies-widget-fr .c3 .pw-card-before{background:#43a047;}
#pharmacies-widget-fr .c4 .pw-card-before{background:#fb8c00;}
#pharmacies-widget-fr .c5 .pw-card-before{background:#8e24aa;}
#pharmacies-widget-fr .c6 .pw-card-before{background:#00897b;}
#pharmacies-widget-fr .pw-badge{
display:inline-block;
padding:4px 10px;
border-radius:20px;
font-size:12px;
font-weight:700;
white-space:nowrap;
margin-bottom:8px;
}
#pharmacies-widget-fr .pw-badge.ok{background:#e8f5e9;color:#2e7d32;}
#pharmacies-widget-fr .pw-badge.partial{background:#fff3e0;color:#e65100;}
#pharmacies-widget-fr .pw-badge.unknown{background:#eceff1;color:#37474f;}
#pharmacies-widget-fr .pw-title{
font-size:17px;
font-weight:800;
color:#1a1a2e;
line-height:1.4;
padding-right:50px;
word-break:break-word;
margin-bottom:10px;
}
#pharmacies-widget-fr .pw-pill{
position:absolute;
top:14px;
right:14px;
width:40px;
height:40px;
border-radius:50%;
text-align:center;
line-height:40px;
color:#fff;
font-size:16px;
font-weight:700;
}
#pharmacies-widget-fr .c1 .pw-pill{background:#e53935;}
#pharmacies-widget-fr .c2 .pw-pill{background:#1e88e5;}
#pharmacies-widget-fr .c3 .pw-pill{background:#43a047;}
#pharmacies-widget-fr .c4 .pw-pill{background:#fb8c00;}
#pharmacies-widget-fr .c5 .pw-pill{background:#8e24aa;}
#pharmacies-widget-fr .c6 .pw-pill{background:#00897b;}
#pharmacies-widget-fr .pw-info{
margin-top:5px;
}
#pharmacies-widget-fr .pw-row{
font-size:13px;
color:#444;
line-height:1.6;
word-break:break-word;
overflow:hidden;
margin-bottom:5px;
}
#pharmacies-widget-fr .pw-ico{
display:inline-block;
width:20px;
text-align:center;
font-size:13px;
vertical-align:top;
}
#pharmacies-widget-fr .pw-row-text{
display:inline;
}
#pharmacies-widget-fr .pw-row a{
color:#1565c0;
font-weight:700;
text-decoration:none;
}
#pharmacies-widget-fr .pw-row a:hover{
text-decoration:underline;
}
#pharmacies-widget-fr .pw-map-btn{
display:block;
text-align:center;
padding:10px 14px;
border-radius:10px;
font-size:14px;
font-weight:700;
text-decoration:none;
color:#fff;
margin-top:12px;
border:none;
cursor:pointer;
}
#pharmacies-widget-fr .pw-map-btn:hover{
opacity:0.88;
}
#pharmacies-widget-fr .c1 .pw-map-btn{background:#e53935;}
#pharmacies-widget-fr .c2 .pw-map-btn{background:#1e88e5;}
#pharmacies-widget-fr .c3 .pw-map-btn{background:#43a047;}
#pharmacies-widget-fr .c4 .pw-map-btn{background:#fb8c00;}
#pharmacies-widget-fr .c5 .pw-map-btn{background:#8e24aa;}
#pharmacies-widget-fr .c6 .pw-map-btn{background:#00897b;}
#pharmacies-widget-fr .pw-footer{
margin-top:28px;
background:#fff;
border-radius:12px;
padding:18px 20px;
font-size:14px;
color:#555;
line-height:1.8;
box-shadow:0 2px 8px rgba(0,0,0,0.05);
}
#pharmacies-widget-fr .pw-footer strong{
display:block;
font-size:15px;
color:#1a1a2e;
margin-bottom:8px;
}
#pharmacies-widget-fr .pw-loading,
#pharmacies-widget-fr .pw-empty,
#pharmacies-widget-fr .pw-error{
background:#fff;
border-radius:14px;
padding:30px 20px;
text-align:center;
box-shadow:0 4px 14px rgba(0,0,0,0.07);
width:100%;
font-size:15px;
color:#555;
}
#pharmacies-widget-fr .pw-error{
color:#c62828;
background:#ffebee;
}
#pharmacies-widget-fr .pw-spinner{
width:40px;
height:40px;
border:4px solid #e3f2fd;
border-top-color:#1e88e5;
border-radius:50%;
margin:0 auto 14px;
-webkit-animation:pwspin 0.9s linear infinite;
animation:pwspin 0.9s linear infinite;
}
@-webkit-keyframes pwspin{
to{-webkit-transform:rotate(360deg);transform:rotate(360deg);}
}
@keyframes pwspin{
to{transform:rotate(360deg);}
}
(function(){
var CONFIG = {
city: "Casablanca",
country: "Morocco",
limit: 6,
radius: 6000
};
var statusBox = document.getElementById("pw-status-fr");
var grid = document.getElementById("pw-grid-fr");
var dateBox = document.getElementById("pw-date-fr");
var colors = ["c1","c2","c3","c4","c5","c6"];
var ICONS = {
pill: "Rx",
pin: "\u25CF",
home: "\u2302",
dist: "\u2194",
phone: "\u260E",
clock: "\u25F4",
web: "\u2197",
map: "\u279C"
};
function esc(s) {
var d = document.createElement("div");
d.appendChild(document.createTextNode(s || ""));
return d.innerHTML;
}
function setStatus(m) {
if (statusBox) {
statusBox.innerHTML = "";
statusBox.appendChild(document.createTextNode(m));
}
}
function formatDate() {
if (!dateBox) return;
var n = new Date();
var days = ["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"];
var months = ["janvier","fevrier","mars","avril","mai","juin","juillet","aout","septembre","octobre","novembre","decembre"];
dateBox.appendChild(document.createTextNode(days[n.getDay()] + " " + n.getDate() + " " + months[n.getMonth()] + " " + n.getFullYear()));
}
function haversine(a, b, c, d) {
var R = 6371;
var dL = (c - a) * Math.PI / 180;
var dN = (d - b) * Math.PI / 180;
var x = Math.sin(dL/2) * Math.sin(dL/2) + Math.cos(a * Math.PI / 180) * Math.cos(c * Math.PI / 180) * Math.sin(dN/2) * Math.sin(dN/2);
return R * (2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x)));
}
function mapUrl(lat, lon) {
return "https://www.google.com/maps/search/?api=1&query=" + encodeURIComponent(lat + "," + lon);
}
function badge(item) {
if (item.opening_hours) return {cls: "ok", label: "Horaires disponibles"};
if (item.phone) return {cls: "partial", label: "Infos partielles"};
return {cls: "unknown", label: "Infos limitees"};
}
function makeXHR(method, url, data, callback) {
var xhr;
if (typeof XMLHttpRequest !== "undefined") {
xhr = new XMLHttpRequest();
} else {
try { xhr = new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch(e1) {
try { xhr = new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch(e2) {
try { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e3) {
callback("Navigateur non supporte", null);
return;
}
}
}
}
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
try {
var json = JSON.parse(xhr.responseText);
callback(null, json);
} catch(e) {
callback("Erreur de parsing", null);
}
} else {
callback("Erreur reseau: " + xhr.status, null);
}
}
};
xhr.open(method, url, true);
if (method === "POST" && data) {
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
}
xhr.send(data || null);
}
function geocode(city, country, callback) {
var url = "https://nominatim.openstreetmap.org/search?format=jsonv2&limit=1&city=" + encodeURIComponent(city) + "&country=" + encodeURIComponent(country);
makeXHR("GET", url, null, function(err, data) {
if (err) { callback(err, null); return; }
if (!data || !data.length) { callback("Ville introuvable", null); return; }
callback(null, {lat: parseFloat(data[0].lat), lon: parseFloat(data[0].lon)});
});
}
function overpass(lat, lon, radius, callback) {
var q = '[out:json][timeout:25];(node["amenity"="pharmacy"](around:' + radius + ',' + lat + ',' + lon + ');way["amenity"="pharmacy"](around:' + radius + ',' + lat + ',' + lon + ');relation["amenity"="pharmacy"](around:' + radius + ',' + lat + ',' + lon + '););out center tags;';
makeXHR("POST", "https://overpass-api.de/api/interpreter", "data=" + encodeURIComponent(q), function(err, data) {
if (err) { callback(err, null); return; }
var elements = (data && data.elements) ? data.elements : [];
callback(null, elements);
});
}
function normalize(item, cLat, cLon) {
var t = item.tags || {};
var lat = item.lat || (item.center ? item.center.lat : null);
var lon = item.lon || (item.center ? item.center.lon : null);
if (!lat || !lon) return null;
var addr = [];
if (t["addr:housenumber"]) addr.push(t["addr:housenumber"]);
if (t["addr:street"]) addr.push(t["addr:street"]);
var addrStr = addr.join(" ");
var city = t["addr:city"] || t["addr:town"] || t["addr:village"] || CONFIG.city;
var fullAddr = addrStr ? (addrStr + ", " + city) : city;
return {
name: t.name || "Pharmacie",
phone: t.phone || t["contact:phone"] || "",
opening_hours: t.opening_hours || "",
website: t.website || t["contact:website"] || "",
city: city,
full_address: fullAddr || "Adresse non disponible",
lat: parseFloat(lat),
lon: parseFloat(lon),
distance: haversine(cLat, cLon, parseFloat(lat), parseFloat(lon)).toFixed(2),
map_url: mapUrl(lat, lon)
};
}
function render(items) {
if (!grid) return;
if (!items.length) {
grid.innerHTML = '
Aucune pharmacie trouvee dans cette zone.
';
return;
}
var html = "";
var i, it, cl, b, web, ph;
for (i = 0; i < items.length; i++) {
it = items[i];
cl = colors[i % colors.length];
b = badge(it);
web = "";
if (it.website) {
web = '
';
}
ph = "";
if (it.phone) {
var cleanPhone = it.phone.replace(/[^\d+]/g, "");
ph = '
' + esc(it.phone) + '';
} else {
ph = 'Telephone non disponible';
}
var hours = it.opening_hours ? esc(it.opening_hours) : "Horaires non disponibles";
html += '
'
+ '
'
+ '
' + ICONS.pill + '
'
+ '
' + esc(b.label) + '
'
+ '
' + esc(it.name) + '
'
+ '
'
+ '
' + ICONS.pin + '' + esc(it.city) + '
'
+ '
' + ICONS.home + '' + esc(it.full_address) + '
'
+ '
' + ICONS.dist + '' + esc(it.distance) + ' km
'
+ '
' + ICONS.phone + '' + ph + '
'
+ '
' + ICONS.clock + '' + hours + '
'
+ web
+ '
'
+ '
' + ICONS.map + ' Voir sur la carte'
+ '
';
}
grid.innerHTML = html;
}
function init() {
formatDate();
setStatus("Recherche de la ville configuree...");
geocode(CONFIG.city, CONFIG.country, function(err, geo) {
if (err) {
grid.innerHTML = '
' + esc(err) + '
';
setStatus("Impossible de charger les donnees.");
return;
}
setStatus("Chargement des pharmacies autour de " + CONFIG.city + "...");
overpass(geo.lat, geo.lon, CONFIG.radius, function(err2, raw) {
if (err2) {
grid.innerHTML = '
' + esc(err2) + '
';
setStatus("Impossible de charger les donnees.");
return;
}
var items = [];
var j, n;
for (j = 0; j CONFIG.limit) {
items = items.slice(0, CONFIG.limit);
}
render(items);
setStatus(items.length + " pharmacie(s) chargee(s) pour " + CONFIG.city + ".");
});
});
}
if (document.readyState === "loading") {
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", init);
} else if (document.attachEvent) {
document.attachEvent("onreadystatechange", function() {
if (document.readyState !== "loading") init();
});
}
} else {
init();
}
})();